﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;

namespace Stars2XML
{
	enum XMLElement
	{
		StarList,
		Star,
		ID,
		Code,
		Name,
	}

	enum XMLAttribute
	{
		RightAscension,
		Declination,
		Magnitude,
		Catalog,
		Color,
		BV,
		Type,
	}

	enum StarCatalog
	{
		BSC,
		HIP,
		Other
	}

	internal static class ReaderExtensions
	{
		static double[] StarBV = new double[]
		{
			//#FFFFFF
			-0.40, //#9BB2FF; B-V = -0.40; Teff = 113017
			-0.35, //#9EB5FF; B-V = -0.35; Teff = 56701
			-0.30, //#A3B9FF; B-V = -0.30; Teff = 33605
			-0.25, //#AABFFF; B-V = -0.25; Teff = 22695
			-0.20, //#B2C5FF; B-V = -0.20; Teff = 16954
			-0.15, //#BBCCFF; B-V = -0.15; Teff = 13674
			-0.10, //#C4D2FF; B-V = -0.10; Teff = 11677
			-0.05, //#CCD8FF; B-V = -0.05; Teff = 10395
			0.00, //#D3DDFF; B-V = -0.00; Teff = 9531
			0.05, //#DAE2FF; B-V = 0.05; Teff = 8917
			0.10, //#DFE5FF; B-V = 0.10; Teff = 8455
			0.15, //#E4E9FF; B-V = 0.15; Teff = 8084
			0.20, //#E9ECFF; B-V = 0.20; Teff = 7767
			0.25, //#EEEFFF; B-V = 0.25; Teff = 7483
			0.30, //#F3F2FF; B-V = 0.30; Teff = 7218
			0.35, //#F8F6FF; B-V = 0.35; Teff = 6967
			0.40, //#FEF9FF; B-V = 0.40; Teff = 6728
			0.45, //#FFF9FB; B-V = 0.45; Teff = 6500
			0.50, //#FFF7F5; B-V = 0.50; Teff = 6285
			0.55, //#FFF5EF; B-V = 0.55; Teff = 6082
			0.60, //#FFF3EA; B-V = 0.60; Teff = 5895
			0.65, //#FFF1E5; B-V = 0.65; Teff = 5722
			0.70, //#FFEFE0; B-V = 0.70; Teff = 5563
			0.75, //#FFEDDB; B-V = 0.75; Teff = 5418
			0.80, //#FFEBD6; B-V = 0.80; Teff = 5286
			0.85, //#FFE9D2; B-V = 0.85; Teff = 5164
			0.90, //#FFE8CE; B-V = 0.90; Teff = 5052
			0.95, //#FFE6CA; B-V = 0.95; Teff = 4948
			1.00, //#FFE5C6; B-V = 1.00; Teff = 4849
			1.05, //#FFE3C3; B-V = 1.05; Teff = 4755
			1.10, //#FFE2BF; B-V = 1.10; Teff = 4664
			1.15, //#FFE0BB; B-V = 1.15; Teff = 4576
			1.20, //#FFDFB8; B-V = 1.20; Teff = 4489
			1.25, //#FFDDB4; B-V = 1.25; Teff = 4405
			1.30, //#FFDBB0; B-V = 1.30; Teff = 4322
			1.35, //#FFDAAD; B-V = 1.35; Teff = 4241
			1.40, //#FFD8A9; B-V = 1.40; Teff = 4159
			1.45, //#FFD6A5; B-V = 1.45; Teff = 4076
			1.50, //#FFD5A1; B-V = 1.50; Teff = 3989
			1.55, //#FFD29C; B-V = 1.55; Teff = 3892
			1.60, //#FFD096; B-V = 1.60; Teff = 3779
			1.65, //#FFCC8F; B-V = 1.65; Teff = 3640
			1.70, //#FFC885; B-V = 1.70; Teff = 3463
			1.75, //#FFC178; B-V = 1.75; Teff = 3234
			1.80, //#FFB765; B-V = 1.80; Teff = 2942
			1.85, //#FFA94B; B-V = 1.85; Teff = 2579
			1.90, //#FF9523; B-V = 1.90; Teff = 2150
			1.95, //#FF7B00; B-V = 1.95; Teff = 1675
			2.00, //#FF5200; B-V = 2.00; Teff = 1195
		};

		static int[] StarColor = new int[]
		{
			0xFFFFFF,
			0x9BB2FF, //B-V = -0.40; Teff = 113017
			0x9EB5FF, //B-V = -0.35; Teff = 56701
			0xA3B9FF, //B-V = -0.30; Teff = 33605
			0xAABFFF, //B-V = -0.25; Teff = 22695
			0xB2C5FF, //B-V = -0.20; Teff = 16954
			0xBBCCFF, //B-V = -0.15; Teff = 13674
			0xC4D2FF, //B-V = -0.10; Teff = 11677
			0xCCD8FF, //B-V = -0.05; Teff = 10395
			0xD3DDFF, //B-V = -0.00; Teff = 9531
			0xDAE2FF, //B-V = 0.05; Teff = 8917
			0xDFE5FF, //B-V = 0.10; Teff = 8455
			0xE4E9FF, //B-V = 0.15; Teff = 8084
			0xE9ECFF, //B-V = 0.20; Teff = 7767
			0xEEEFFF, //B-V = 0.25; Teff = 7483
			0xF3F2FF, //B-V = 0.30; Teff = 7218
			0xF8F6FF, //B-V = 0.35; Teff = 6967
			0xFEF9FF, //B-V = 0.40; Teff = 6728
			0xFFF9FB, //B-V = 0.45; Teff = 6500
			0xFFF7F5, //B-V = 0.50; Teff = 6285
			0xFFF5EF, //B-V = 0.55; Teff = 6082
			0xFFF3EA, //B-V = 0.60; Teff = 5895
			0xFFF1E5, //B-V = 0.65; Teff = 5722
			0xFFEFE0, //B-V = 0.70; Teff = 5563
			0xFFEDDB, //B-V = 0.75; Teff = 5418
			0xFFEBD6, //B-V = 0.80; Teff = 5286
			0xFFE9D2, //B-V = 0.85; Teff = 5164
			0xFFE8CE, //B-V = 0.90; Teff = 5052
			0xFFE6CA, //B-V = 0.95; Teff = 4948
			0xFFE5C6, //B-V = 1.00; Teff = 4849
			0xFFE3C3, //B-V = 1.05; Teff = 4755
			0xFFE2BF, //B-V = 1.10; Teff = 4664
			0xFFE0BB, //B-V = 1.15; Teff = 4576
			0xFFDFB8, //B-V = 1.20; Teff = 4489
			0xFFDDB4, //B-V = 1.25; Teff = 4405
			0xFFDBB0, //B-V = 1.30; Teff = 4322
			0xFFDAAD, //B-V = 1.35; Teff = 4241
			0xFFD8A9, //B-V = 1.40; Teff = 4159
			0xFFD6A5, //B-V = 1.45; Teff = 4076
			0xFFD5A1, //B-V = 1.50; Teff = 3989
			0xFFD29C, //B-V = 1.55; Teff = 3892
			0xFFD096, //B-V = 1.60; Teff = 3779
			0xFFCC8F, //B-V = 1.65; Teff = 3640
			0xFFC885, //B-V = 1.70; Teff = 3463
			0xFFC178, //B-V = 1.75; Teff = 3234
			0xFFB765, //B-V = 1.80; Teff = 2942
			0xFFA94B, //B-V = 1.85; Teff = 2579
			0xFF9523, //B-V = 1.90; Teff = 2150
			0xFF7B00, //B-V = 1.95; Teff = 1675
			0xFF5200, //B-V = 2.00; Teff = 1195
		};

		internal static void ForEachData(this BinaryReader reader, Action<int> body)
		{
			int count = reader.ReadInt32();
			for (int i = 0; i < count; i++)
				body(i);
		}

		internal static void ReadColor(this BinaryReader reader, out double? bv, out int color)
		{
			var colorIndex = (int)reader.ReadByte();
			bv = colorIndex > 0 ? (double?)StarBV[colorIndex - 1] : null;
			color = StarColor[colorIndex];
		}

		internal static string ReadFlagString(this BinaryReader reader)
		{
			return reader.ReadBoolean() ? reader.ReadString() : null;
		}
	}

	internal static class WriterExtensions
	{
		public const string NS = "urn:denebsoft-skymap-star";

		internal static void WriteDocumentElement(this XmlWriter writer, XMLElement name, Action body)
		{
			writer.WriteStartDocument();
			writer.WriteStartElement(name.ToString(), NS);
			body();
			writer.WriteEndElement();
			writer.WriteEndDocument();
		}

		internal static void WriteElement(this XmlWriter writer, XMLElement name, Action body)
		{
			writer.WriteStartElement(name.ToString(), NS);
			body();
			writer.WriteEndElement();
		}

		internal static void WriteElement(this XmlWriter writer, XMLElement name, int value, Action body)
		{
			writer.WriteStartElement(name.ToString(), NS);
			body();
			writer.WriteString(XmlConvert.ToString(value));
			writer.WriteEndElement();
		}

		internal static void WriteElement(this XmlWriter writer, XMLElement name, string value, Action body)
		{
			if (value == null) return;
			writer.WriteStartElement(name.ToString(), NS);
			body();
			writer.WriteString(value);
			writer.WriteEndElement();
		}

		internal static void WriteAttribute(this XmlWriter writer, XMLAttribute name, float value)
		{
			writer.WriteAttributeString(name.ToString(), XmlConvert.ToString(value));
		}

		internal static void WriteAttribute<TEnum>(this XmlWriter writer, XMLAttribute name, TEnum value, TEnum defValue) where TEnum: struct
		{
			if (!value.Equals(defValue))
				writer.WriteAttributeString(name.ToString(), value.ToString());
		}

		internal static void WriteAttributeColor(this XmlWriter writer, XMLAttribute name, int color)
		{
			writer.WriteAttributeString(name.ToString(), color.ToString("X6"));
		}

		internal static void WriteAttribute(this XmlWriter writer, XMLAttribute name, double? value)
		{
			if (value.HasValue)
				writer.WriteAttributeString(name.ToString(), XmlConvert.ToString(value.Value));
		}

		internal static void WriteAttributeLanguage(this XmlWriter writer, string lang)
		{
			writer.WriteAttributeString("xml", "lang", null, lang);
		}
	}

	class Program
	{
		static void Main(string[] args)
		{
			string ifn1 = args.Length < 2 ? "stars.dat" : args[0];
			string ifn2 = args.Length < 2 ? "starnames.dat" : args[1];
			string ofn = args.Length == 1 ? args[0] : args.Length == 3 ? args[2] : "stars.xml";

			if (args.Length > 3 || Path.GetExtension(ifn1) != ".dat" || Path.GetExtension(ifn2) != ".dat" || Path.GetExtension(ofn) != ".xml")
			{
				Console.WriteLine("usage: {0} [stars.dat starnames.dat] [stars.xml]", Path.GetFileNameWithoutExtension(System.Reflection.Assembly.GetExecutingAssembly().Location));
				return;
			}

			using (var ifs1 = new FileStream(ifn1, FileMode.Open))
			using (var reader1 = new BinaryReader(ifs1))
			using (var ifs2 = new FileStream(ifn2, FileMode.Open))
			using (var reader2 = new BinaryReader(ifs2))
			using (var writer = XmlWriter.Create(ofn, new XmlWriterSettings()
			{
				Encoding = Encoding.UTF8,
				Indent = true,
				IndentChars = "\t",
				OmitXmlDeclaration = false
			}))
			{
				writer.WriteDocumentElement(XMLElement.StarList, () =>
				{
					int count2 = reader2.ReadInt32();
					int i2 = 0;
					string Name = null;
					string BayerFlamsted = null;
					int index = -1;
					reader1.ForEachData(i1 =>
					{
						if (index < i1 && i2 < count2)
						{
							index = reader2.ReadInt32();
							Name = reader2.ReadFlagString();
							BayerFlamsted = reader2.ReadFlagString();
							i2++;
						}
						writer.WriteElement(XMLElement.Star, () =>
						{
							float magnitude = reader1.ReadSingle();
							float RA = reader1.ReadSingle();
							float Dec = reader1.ReadSingle();
							int ID = reader1.ReadInt32();
							var Catalog = (StarCatalog)reader1.ReadByte();
							double? BV; int ColorHex;
							reader1.ReadColor(out BV, out ColorHex);

							writer.WriteAttribute(XMLAttribute.RightAscension, RA); //Right Ascension
							writer.WriteAttribute(XMLAttribute.Declination, Dec);
							writer.WriteAttribute(XMLAttribute.Magnitude, magnitude);
							//writer.WriteAttribute(XMLAttribute.Catalog, Catalog);
							writer.WriteAttributeColor(XMLAttribute.Color, ColorHex);
							writer.WriteAttribute(XMLAttribute.BV, BV); //B-V index

							writer.WriteElement(XMLElement.ID, ID, () =>
							{
								writer.WriteAttribute(XMLAttribute.Type, Catalog, StarCatalog.Other);
							});

							if (index == i1)
							{
								writer.WriteElement(XMLElement.Code, BayerFlamsted, () =>
								{
									writer.WriteAttribute(XMLAttribute.Type, StarCatalog.BSC, StarCatalog.Other);
								});
								writer.WriteElement(XMLElement.Name, Name, () =>
								{
									writer.WriteAttributeLanguage("la");
								});
							}
						});
					});
				});
			}
		}
	}
}
